using System;
using System.Collections.Generic;
using System.Text;
using Ares.PacketHandlers;
using ZLib;

namespace cb0tServer
{
    partial class ServerCore
    {
        private void ProcessLinkPacket(byte id, AresDataPacket packet)
        {
            String name, text;
            UserObject user;
            LinkedUserObject lUser;
            ushort build;

            if (packet.GetByteCount() > 4090)
            {
                this.link_client.PartAndDisconnect();
                return;
            }

            switch (id)
            {
                case 5: // update (admin change)
                    name = packet.ReadString();
                    lUser = this.link_client.GetUser(name);

                    if (lUser != null)
                    {
                        packet.SkipBytes(13); // manually position reader to where the admin byte SHOULD be !!!
                        lUser.admin = (packet.ReadByte() >= 1);

                        if (lUser.visible)
                            UserPool.Broadcast(0, ServerOutboundPackets.UpdatePacket(lUser));
                    }

                    break;

                case 9: // avatar
                    name = packet.ReadString();
                    lUser = this.link_client.GetUser(name);

                    if (lUser != null)
                    {
                        lUser.avatar_count++;

                        if (lUser.avatar_count > 2)
                        {
                            lUser.avatar_count = 10;
                            return;
                        }

                        lUser.avatar = packet.ReadBytes();

                        if (lUser.visible)
                            UserPool.Broadcast(0, ServerOutboundPackets.AvatarPacket(lUser));
                    }

                    break;

                case 13: // personal message
                    name = packet.ReadString();
                    lUser = this.link_client.GetUser(name);

                    if (lUser != null)
                    {
                        lUser.personal_message = packet.ReadString();

                        if (lUser.visible)
                            UserPool.Broadcast(0, ServerOutboundPackets.PersonalMessagePacket(lUser));
                    }

                    break;

                case 10: // public text
                    name = packet.ReadString();
                    text = packet.ReadString();
                    lUser = this.link_client.GetUser(name);
                    Helpers.AddLinkFloodText();

                    if (lUser != null)
                        text = this.LinkFilterText(lUser, text);

                    if (String.IsNullOrEmpty(text))
                        return;

                    UserPool.BroadcastUnignored(name, ServerOutboundPackets.PublicTextPacket(name, text));
                    ChatHistory.AddLine("\x000301" + name + "> \x000312" + text);
                    SCASPool.Broadcast(SCASOutboundPackets.LinkPublicText(name, text));

                    if (CoreSettings.chat_logging)
                        OtherLogs.WriteChatLog("linked user: " + name + "> " + text);

                    break;

                case 11: // emote text
                    name = packet.ReadString();
                    text = packet.ReadString();
                    lUser = this.link_client.GetUser(name);
                    Helpers.AddLinkFloodText();

                    if (lUser != null)
                        text = this.LinkFilterText(lUser, text);

                    if (String.IsNullOrEmpty(text))
                        return;

                    UserPool.BroadcastUnignored(name, ServerOutboundPackets.EmoteTextPacket(name, text));
                    ChatHistory.AddLine("\x000306* " + name + " " + text);
                    SCASPool.Broadcast(SCASOutboundPackets.LinkEmoteText(name, text));

                    if (CoreSettings.chat_logging)
                        OtherLogs.WriteChatLog("linked user: * " + name + " " + text);

                    break;

                case 20: // join
                    lUser = new LinkedUserObject();
                    lUser.file_count = packet.ReadInt16();
                    lUser.line_type = packet.ReadInt32();
                    lUser.external_ip = packet.ReadIP();
                    lUser.dc_port = packet.ReadInt16();
                    lUser.node_ip = packet.ReadIP();
                    lUser.node_port = packet.ReadInt16();
                    packet.SkipByte();
                    lUser.name = packet.ReadString();
                    lUser.local_ip = packet.ReadIP();
                    lUser.browse = packet.ReadByte() == 1;

                    if (!lUser.browse)
                        lUser.file_count = 0;

                    lUser.admin = packet.ReadByte() >= 1;

                    if (packet.Remaining() > 0)
                        lUser.version = packet.ReadString();
                    else
                        lUser.version = "";

                    if (packet.Remaining() > 3)
                    {
                        lUser.age = packet.ReadByte();
                        lUser.sex = packet.ReadByte();
                        lUser.location = packet.ReadByte();
                        lUser.region = packet.ReadString();
                    }

                    if (this.link_client.AddNewLinkedUser(lUser))
                    {
                        user = UserPool.GetVroom0UserByName(lUser.name);

                        if (user == null) // no regular user by that name
                        {
                            lUser.visible = true;
                            UserPool.Broadcast(0, ServerOutboundPackets.LinkJoinPacket(lUser));
                            SCASPool.Broadcast(SCASOutboundPackets.LinkJoinedItem(lUser));
                        }
                    }

                    break;

                case 22: // part
                    name = this.link_client.RemoveLinkedUser(packet.ReadString());

                    if (name != null)
                    {
                        user = UserPool.GetVroom0UserByName(name);

                        if (user == null) // only delete if no regular user
                        {
                            UserPool.RemoveLinkIgnore(name);
                            UserPool.Broadcast(0, ServerOutboundPackets.LinkPartPacket(name));
                            SCASPool.Broadcast(SCASOutboundPackets.LinkPartedItem(name));
                        }
                    }

                    break;

                case 30: // userlist item
                    lUser = new LinkedUserObject();
                    lUser.file_count = packet.ReadInt16();
                    lUser.line_type = packet.ReadInt32();
                    lUser.external_ip = packet.ReadIP();
                    lUser.dc_port = packet.ReadInt16();
                    lUser.node_ip = packet.ReadIP();
                    lUser.node_port = packet.ReadInt16();
                    packet.SkipByte();
                    lUser.name = packet.ReadString();
                    lUser.local_ip = packet.ReadIP();
                    lUser.browse = packet.ReadByte() == 1;

                    if (!lUser.browse)
                        lUser.file_count = 0;

                    lUser.admin = packet.ReadByte() >= 1;

                    if (packet.Remaining() > 0)
                        lUser.version = packet.ReadString();
                    else
                        lUser.version = "";

                    if (packet.Remaining() > 3)
                    {
                        lUser.age = packet.ReadByte();
                        lUser.sex = packet.ReadByte();
                        lUser.location = packet.ReadByte();
                        lUser.region = packet.ReadString();
                    }

                    if (this.link_client.AddNewLinkedUser(lUser))
                    {
                        user = UserPool.GetVroom0UserByName(lUser.name);

                        if (user == null) // no regular user by that name
                        {
                            lUser.visible = true;
                            UserPool.Broadcast(0, ServerOutboundPackets.LinkJoinPacket(lUser));
                            SCASPool.Broadcast(SCASOutboundPackets.LinkJoinedItem(lUser));
                        }
                    }

                    break;

                case 52: // browse request
                    String _browse_requester = packet.ReadString(); // person requesting files
                    ushort _browse_ident = packet.ReadInt16(); // ident
                    bool _should_compress = (packet.ReadByte() == 80); // zlib
                    byte _browse_mime = packet.ReadByte(); // type
                    String _browse_target = packet.ReadString(); // person with files
                    user = UserPool.GetUserByName(_browse_target);
                    FileBrowseHelpers.ProcessLinkedBrowseRequest(this.link_client, _browse_requester, user, _browse_ident, _browse_mime, _should_compress);
                    break;

                case 102: // link error
                    byte error_code = packet.ReadByte();

                    switch (error_code)
                    {
                        case (byte)LinkClient.LinkError.BannedUser:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: you are banned from linking with this room!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        case (byte)LinkClient.LinkError.LinkingDisabled:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: target room's linking is disabled!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        case (byte)LinkClient.LinkError.OldProtocol:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: your link protocol is too old for target room!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        case (byte)LinkClient.LinkError.NoInbound:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: target room is not accepting inbound link requests!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        case (byte)LinkClient.LinkError.WrongLanguage:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: target room does not support english!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        case (byte)LinkClient.LinkError.NotOnAllowList:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: your room is not on the target room's allow list!"));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;

                        default:
                            this.link_client.PartAndDisconnect();
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: type " + error_code));
                            UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                            return;
                    }

                case 103: // broadcast (packet within)
                    packet.SkipBytes(2); // don't care about length

                    if (packet.ReadByte() == 44) // announce text
                    {
                        text = packet.ReadString();
                        Helpers.AddLinkFloodText();
                        UserPool.Broadcast(0, ServerOutboundPackets.AnnoucePacket(text));
                    }

                    break;

                case 104: // relay to user (packet within)
                    String _from = packet.ReadString();
                    packet.SkipBytes(2);
                    byte _inner_id = packet.ReadByte();

                    switch (_inner_id)
                    {
                        case 25: // pm
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                if (Settings.pm_block.Contains(user.name))
                                    return;

                                name = packet.ReadString();
                                text = packet.ReadString();
                                lUser = this.link_client.GetUser(name);

                                if (lUser != null)
                                {
                                    if (!this.LinkFilterPM(lUser, text)) // filtered
                                        return;

                                    if (user.ignores.Contains(name)) // ignored
                                        return;
                                }

                                user.SendOutboundPacket(ServerOutboundPackets.PMPacket(name, text));
                            }

                            break;

                        case 44: // announce to one user
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                text = packet.ReadString();
                                user.SendOutboundPacket(ServerOutboundPackets.AnnoucePacket(text));
                            }

                            break;

                        case 53: // browse end
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                build = packet.ReadInt16();
                                user.SendOutboundPacket(ServerOutboundPackets.BrowseEndPacket(build));
                            }

                            break;

                        case 54: // browse fail
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                build = packet.ReadInt16();
                                user.SendOutboundPacket(ServerOutboundPackets.BrowseFailPacket(build));
                            }

                            break;

                        case 55: // browse item
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                SharedFileObject _file = new SharedFileObject();
                                build = packet.ReadInt16();
                                _file.type = packet.ReadByte();
                                _file.size = packet.ReadInt32();
                                _file.content = packet.ReadBytes();

                                byte[] _p1 = ServerOutboundPackets.BrowseItemPacket(build, _file);

                                if (user.supports_compression)
                                    _p1 = ServerOutboundPackets.CompressPacket(_p1);

                                user.SendOutboundPacket(_p1);
                            }

                            break;

                        case 56: // browse start
                            user = UserPool.GetUserByName(_from);

                            if (user != null)
                            {
                                build = packet.ReadInt16();
                                ushort _file_count = packet.ReadInt16();
                                user.SendOutboundPacket(ServerOutboundPackets.BrowseStartPacket(build, _file_count));
                            }

                            break;

                        case 80: // compressed packet
                            byte[] _decompress;

                            try
                            {
                                _decompress = Zlib.Decompress(packet.ReadBytes(), false);
                            }
                            catch
                            {
                                return;
                            }

                            packet = new AresDataPacket(_decompress);

                            while (packet.Remaining() > 3)
                            {
                                ushort _file_size = packet.ReadInt16();

                                if (packet.ReadByte() != 55)
                                    break; // only interested in browse items

                                user = UserPool.GetUserByName(_from);

                                AresDataPacket _file_packet = new AresDataPacket(packet.ReadBytes(_file_size));

                                if (user != null)
                                {
                                    SharedFileObject _file = new SharedFileObject();
                                    build = _file_packet.ReadInt16();
                                    _file.type = _file_packet.ReadByte();
                                    _file.size = _file_packet.ReadInt32();
                                    _file.content = _file_packet.ReadBytes();

                                    byte[] _p2 = ServerOutboundPackets.BrowseItemPacket(build, _file);

                                    if (user.supports_compression)
                                        _p2 = ServerOutboundPackets.CompressPacket(_p2);

                                    user.SendOutboundPacket(_p2);
                                }
                            }

                            break;
                    }

                    break;

                case 107: // pong received
                    this.link_client.last_update_received_time = this.time_now;
                    break;

                case 113: // we are client - server ACK's us
                    packet.SkipBytes(2);
                    build = packet.ReadInt16();

                    if (build < 3041) // minimum proto version
                    {
                        this.link_client.PartAndDisconnect();
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314link error: target room's link protocol is too old!"));
                        UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: disconnected"));
                        return;
                    }

                    packet.SkipBytes(2);
                    this.link_client.room_name = packet.ReadString();
                    UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314room link: accepted link from " + this.link_client.room_name + " (Y)"));
                    this.link_client.logged_in = true;
                    this.link_client.SetRoomInfo();

                    foreach (UserObject usr in UserPool.Users)
                        if (usr.IsSet())
                            if (usr.logged_in)
                                if (usr.vroom == 0)
                                    this.link_client.SendOutboundPacket(ServerOutboundPackets.UserListItemPacket(usr, true));

                    this.link_client.SendOutboundPacket(ServerOutboundPackets.UserListEndPacket());

                    foreach (UserObject usr in UserPool.Users)
                    {
                        if (usr.IsSet())
                        {
                            if (usr.logged_in)
                            {
                                if (usr.vroom == 0)
                                {
                                    this.link_client.SendOutboundPacket(ServerOutboundPackets.AvatarPacket(usr));
                                    this.link_client.SendOutboundPacket(ServerOutboundPackets.PersonalMessagePacket(usr, usr.user_message));
                                }
                            }
                        }
                    }

                    break;

                case 120: // admin command received (custom sbot)
              //      byte[] _link_command_buffer = packet.ReadBytes();
              //      _link_command_buffer = Ares.Crypt.AresCrypt.d67(_link_command_buffer, (int)CoreSettings.room_port); // my port
              //      _link_command_buffer = Ares.Crypt.AresCrypt.d67(_link_command_buffer, (int)this.link_client.target_port); // their port
              //      packet = new AresDataPacket(_link_command_buffer);
                    name = packet.ReadString();

                    if (!Settings.admins || !CoreSettings.allow_linked_admins) // not allowing linked admin's command
                    {
                        this.link_client.SendOutboundPacket(ServerOutboundPackets.LinkAdminDisabled(name));
                        return;
                    }

                    lUser = this.link_client.GetUser(name);

                    if (lUser == null)
                    {
                        return; // can't find the linked admin!
                    }

                    if (!lUser.admin)
                    {
                        return; // linked admin isn't recognised as an admin!
                    }

                    user = UserPool.GetVroom0UserByName(packet.ReadString());
                    text = packet.ReadString();
                    this.OnLinkedAdminCommand(lUser, text, user, packet.ReadString());
                    break;

                case 121: // admin disabled received (custom sbot)
                    name = packet.ReadString();
                    UserPool.Broadcast(ServerOutboundPackets.AnnoucePacket("\x000314" + name + ", linked admin is disabled in " + this.link_client.room_name));
                    break;

                case 122: // message to admins (custom sbot)
                    text = packet.ReadString();
                    UserPool.BroadcastToAdmins(ServerOutboundPackets.AnnoucePacket(text));
                    break;

                default:
                    break;
            }
        }
    }
}
